package com.globefrog.framework.log;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.globefrog.framework.common.constant.LogConstants;
import com.globefrog.framework.log.util.ExceptionUtils;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

/**
 * 追踪日志.
 *
 * @author caijinbang
 * @date 2020-11-05 21:03
 */
public class TraceLogger {


  /**
   * 线程id和日志处理器.
   */
  private static ConcurrentMap<Long, LogEventProcessor> processors = new ConcurrentHashMap<>();
  /**
   * 输出格式布局.
   */
  private PatternLayout layout;
  /**
   * 日志目录.
   */
  private String logFolder;

  private TraceLogger() {
  }


  private static class TraceLoggerHolder {

    private static final TraceLogger INSTANCE = new TraceLogger();
  }

  public static TraceLogger getInstance() {
    return TraceLoggerHolder.INSTANCE;
  }


  /**
   * 处理traceLog事件.
   *
   * @param event 事件
   * @throws IOException IO异常
   */
  protected void process(ILoggingEvent event) throws IOException {
    // 目标线程id
    long threadId = getTargetThreadId();
    LogEventProcessor processor = processors.get(threadId);
    // 允许tracelog日志输出且初始化过traceLog
    if (LogSettings.getInstance().isEnableTraceLog() && processor != null) {
      processor.process(event);
    }
  }

  /**
   * 从MDC中获取目标线程id.
   *
   * @return 目标线程id
   */
  private long getTargetThreadId() {
    String targetThreadId = MDC.get(LogConstants.MDC_TARGET_THREAD_ID);
    boolean numeric = StringUtils.isNumeric(targetThreadId);
    return numeric ? Long.parseLong(targetThreadId) : Thread.currentThread().getId();
  }

  /**
   * 初始化.
   */
  public void initialize() {
    // 清除MDC
    MDC.clear();
    long threadId = Thread.currentThread().getId();
    // 初始化线程id和日志事件处理器
    processors.put(threadId, new LogEventProcessor(this.layout, this.logFolder));
  }

  public void cleanup(boolean forceFlushTraceLog) {
    try {
      long threadId = Thread.currentThread().getId();
      LogEventProcessor processor = processors.remove(threadId);
      processor.cleanup(forceFlushTraceLog || LogSettings.getInstance().isAlwaysWriteTraceLog());
    } catch (IOException e) {
      System.err.println("failed to clean up TraceLog, exception=" + ExceptionUtils.stackTrace(e));
      throw new RuntimeException(e);
    }
  }

  protected void clearAll() {
    processors.clear();
  }

  public void setLayout(PatternLayout layout) {
    this.layout = layout;
  }

  public void setLogFolder(String logFolder) {
    this.logFolder = logFolder;
  }
}
